﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.Mvc;
using System.Web.UI;
using System.Diagnostics;
using System.Web;

namespace Lilium.Web.Mvc
{
	/// <summary>
	/// 
	/// </summary>
	/// <remarks>
	/// Thread Safety
	/// This type is thread safe.
	/// </remarks>
	public class OutputCachePolicy
	{
		public OutputCachePolicy(OutputCacheLocation location, int duration)
		{
			m_Duration = duration;
			switch (location)
			{
				case OutputCacheLocation.Any:
					m_ApplyCachePolicyFun = context =>
					{
						context.HttpContext.Response.Cache.SetCacheability(HttpCacheability.Public);
						InitCacheProperties(context);
					};
					break;
				case OutputCacheLocation.Client:
					m_ApplyCachePolicyFun = context =>
					{
						context.HttpContext.Response.Cache.SetCacheability(HttpCacheability.Private);
						InitCacheProperties(context);
					};
					break;
				case OutputCacheLocation.Downstream:
					m_ApplyCachePolicyFun = context =>
					{
						var cache = context.HttpContext.Response.Cache;
						cache.SetCacheability(HttpCacheability.Public);
						cache.SetNoServerCaching();
						InitCacheProperties(context);
					};
					break;
				case OutputCacheLocation.Server:
					m_ApplyCachePolicyFun = context =>
					{
						context.HttpContext.Response.Cache.SetCacheability(HttpCacheability.Server);
						InitCacheProperties(context);
					};
					break;
				case OutputCacheLocation.None:
					m_ApplyCachePolicyFun = context => context.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
					break;
				case OutputCacheLocation.ServerAndClient:
					m_ApplyCachePolicyFun = context =>
					{
						context.HttpContext.Response.Cache.SetCacheability(HttpCacheability.ServerAndPrivate);
						InitCacheProperties(context);
					};
					break;
				default:
					throw new ArgumentOutOfRangeException("location", string.Format(
						Resources.Culture,
						Resources.ResourceCachePolicy_InvalidCacheLocation_0location,
						location));
			}
		}

		public void Apply(ControllerContext context)
		{
			if (context == null)
				throw new ArgumentNullException("context");

			ApplyCachePolicyFun(context);
		}

		private void InitCacheProperties(ControllerContext context)
		{
			var cache = context.HttpContext.Response.Cache;
			var timestamp = context.HttpContext.Timestamp;
			cache.SetExpires(timestamp.AddSeconds(Duration));
			cache.SetMaxAge(new TimeSpan(0, 0, Duration));
			cache.SetValidUntilExpires(true);
			cache.SetLastModified(timestamp);
		}

		#region private double Duration

		private readonly int m_Duration;

		private int Duration { [DebuggerNonUserCodeAttribute] get { return m_Duration; } }

		#endregion

		#region private Action<HttpCachePolicyBase> ApplyCachePolicyFun

		private readonly Action<ControllerContext> m_ApplyCachePolicyFun;

		private Action<ControllerContext> ApplyCachePolicyFun
		{
			get
			{
				return m_ApplyCachePolicyFun;
			}
		}

		#endregion
	}
}